package com.xyzwps.lib.express.router.ap;

import com.xyzwps.lib.openapi.element.Schema;
import com.xyzwps.lib.openapi.element.SchemaFormat;
import com.xyzwps.lib.openapi.element.SchemaVisitor1;

import static com.xyzwps.lib.dollar.Dollar.$;
import static com.xyzwps.lib.dollar.Dollar.$.unescape;

public record OpenApiSchemaToSourceCodeVisitor(Lines lines) implements SchemaVisitor1<String> {

    @Override
    public void visit(Schema.RefSchema it, String varName) {
        lines.add("var " + varName + " = new Schema.RefSchema(\"" + unescape(it.$ref()) + "\");");
    }

    @Override
    public void visit(Schema.AllOf it, String varName) {
        lines.add("var " + varName + " = new Schema.AllOf();");
        lines.add(varName + ".title(\"" + unescape(it.title()) + "\");");
        lines.add(varName + ".description(\"" + unescape(it.description()) + "\");");
        lines.add(varName + ".deprecated(" + it.deprecated() + ");");

        if ($.isNotEmpty(it.schemas())) {
            lines.add("{");
            lines.indentPP();
            int i = 0;
            for (var s : it.schemas()) {
                var name = "allOf" + i++;
                s.accept(this, name);
                lines.add(varName + ".add(" + name + ");");
            }
            lines.indentPP();
            lines.add("}");
        }
    }

    @Override
    public void visit(Schema.OneOf it, String varName) {
        lines.add("var " + varName + " = new Schema.OneOf();");
        lines.add(varName + ".title(\"" + unescape(it.title()) + "\");");
        lines.add(varName + ".description(\"" + unescape(it.description()) + "\");");
        lines.add(varName + ".deprecated(" + it.deprecated() + ");");

        if ($.isNotEmpty(it.schemas())) {
            lines.add("{");
            lines.indentPP();
            int i = 0;
            for (var s : it.schemas()) {
                var name = "oneOf" + i++;
                s.accept(this, name);
                lines.add(varName + ".add(" + name + ");");
            }
            lines.indentPP();
            lines.add("}");
        }
    }

    @Override
    public void visit(Schema.AnyOf it, String varName) {
        lines.add("var " + varName + " = new Schema.AnyOf();");
        lines.add(varName + ".title(\"" + unescape(it.title()) + "\");");
        lines.add(varName + ".description(\"" + unescape(it.description()) + "\");");
        lines.add(varName + ".deprecated(" + it.deprecated() + ");");

        if ($.isNotEmpty(it.schemas())) {
            lines.add("{");
            lines.indentPP();
            int i = 0;
            for (var s : it.schemas()) {
                var name = "anyOf" + i++;
                s.accept(this, name);
                lines.add(varName + ".add(" + name + ");");
            }
            lines.indentPP();
            lines.add("}");
        }
    }

    @Override
    public void visit(Schema.ObjectSchema it, String varName) {
        lines.add("var " + varName + " = new Schema.ObjectSchema();");
        lines.add(varName + ".title(\"" + unescape(it.title()) + "\");");
        lines.add(varName + ".description(\"" + unescape(it.description()) + "\");");
        lines.add(varName + ".deprecated(" + it.deprecated() + ");");

        for (var required : it.required()) {
            lines.add(varName + ".addRequired(" + unescape(required) + ");");
        }

        lines.add(varName + ".maxProperties(" + it.maxProperties() + ");");
        lines.add(varName + ".minProperties(" + it.minProperties() + ");");

        if ($.isNotEmpty(it.properties())) {
            lines.add("{").indentPP();
            it.properties().forEach((name, s) -> {
                var propVarName = "prop_" + name;
                s.accept(this, propVarName);
                lines.add(varName + ".addProperty(" + unescape(name) + ", " + propVarName + ");");
            });
            lines.indentMM().add("}");
        }

        var ap = it.additionalProperties();
        if (ap != null) {
            ap.accept(this, "additionalProperties");
            lines.add(varName + ".additionalProperties(additionalProperties);");
        }

        // TODO: example
        // TODO: discriminator
    }

    @Override
    public void visit(Schema.ArraySchema it, String varName) {
        lines.add("var " + varName + " = new Schema.ArraySchema();");
        lines.add(varName + ".title(\"" + unescape(it.title()) + "\");");
        lines.add(varName + ".description(\"" + unescape(it.description()) + "\");");
        lines.add(varName + ".deprecated(" + it.deprecated() + ");");

        it.items().accept(this, "items");
        lines.add(varName + ".items(items);");

        // TODO: example

        lines.add(varName + ".maxItems(" + it.maxItems() + ");");
        lines.add(varName + ".minItems(" + it.minItems() + ");");
        lines.add(varName + ".uniqueItems(" + it.uniqueItems() + ");");
        lines.add(varName + ".maxContains(" + it.maxContains() + ");");
        lines.add(varName + ".minContains(" + it.minContains() + ");");
    }

    @Override
    public void visit(Schema.EnumSchema it, String varName) {
        lines.add("var " + varName + " = new Schema.EnumSchema();");
        lines.add(varName + ".title(\"" + unescape(it.title()) + "\");");
        lines.add(varName + ".description(\"" + unescape(it.description()) + "\");");
        lines.add(varName + ".deprecated(" + it.deprecated() + ");");

        if ($.isNotEmpty(it.example())) {
            lines.add(varName + ".example(\"" + unescape(it.example()) + "\");");
        }

        for (var v : it.enums()) {
            lines.add(varName + ".addEnum(\"" + unescape(v) + "\");");
        }
    }

    @Override
    public void visit(Schema.StringSchema it, String varName) {
        lines.add("var " + varName + " = new Schema.StringSchema();");
        lines.add(varName + ".title(\"" + unescape(it.title()) + "\");");
        lines.add(varName + ".description(\"" + unescape(it.description()) + "\");");
        lines.add(varName + ".deprecated(" + it.deprecated() + ");");

        if ($.isNotBlank(it.defaultValue())) {
            lines.add(varName + ".defaultValue(\"" + unescape(it.defaultValue()) + "\");");
        }

        var format = it.format();
        if (format != null && format.forType() == SchemaFormat.For.STRING) {
            lines.add(varName + ".format(SchemaFormat." + format + ");");
        }

        if ($.isNotBlank(it.example())) {
            lines.add(varName + ".example(\"" + unescape(it.example()) + "\");");
        }

        lines.add(varName + ".maxLength(" + it.maxLength() + ");");
        lines.add(varName + ".minLength(" + it.minLength() + ");");

        if ($.isNotBlank(it.pattern())) {
            lines.add(varName + ".pattern(" + unescape(it.pattern()) + ");");
        }
    }

    @Override
    public void visit(Schema.BooleanSchema it, String varName) {
        lines.add("var " + varName + " = new Schema.BooleanSchema();");
        lines.add(varName + ".title(\"" + unescape(it.title()) + "\");");
        lines.add(varName + ".description(\"" + unescape(it.description()) + "\");");
        lines.add(varName + ".deprecated(" + it.deprecated() + ");");
    }

    @Override
    public void visit(Schema.NumberSchema it, String varName) {
        lines.add("var " + varName + " = new Schema.NumberSchema();");
        lines.add(varName + ".title(\"" + unescape(it.title()) + "\");");
        lines.add(varName + ".description(\"" + unescape(it.description()) + "\");");
        lines.add(varName + ".deprecated(" + it.deprecated() + ");");
        lines.add(varName + ".defaultValue(" + it.defaultValue() + ");");

        lines.add(varName + ".example(" + it.example() + ");");

        var format = it.format();
        if (format != null && format.forType() == SchemaFormat.For.NUMBER) {
            lines.add(varName + ".format(SchemaFormat." + format + ");");
        }

        lines.add(varName + ".multipleOf(" + it.multipleOf() + ");");
        lines.add(varName + ".maximum(" + it.maximum() + ");");
        lines.add(varName + ".minimum(" + it.minimum() + ");");
        lines.add(varName + ".exclusiveMaximum(" + it.exclusiveMaximum() + ");");
        lines.add(varName + ".exclusiveMinimum(" + it.exclusiveMinimum() + ");");
    }

    @Override
    public void visit(Schema.IntegerSchema it, String varName) {
        lines.add("var " + varName + " = new Schema.IntegerSchema();");
        lines.add(varName + ".title(\"" + unescape(it.title()) + "\");");
        lines.add(varName + ".description(\"" + unescape(it.description()) + "\");");
        lines.add(varName + ".deprecated(" + it.deprecated() + ");");
        if (it.defaultValue() != null) {
            lines.add(varName + ".defaultValue(" + it.defaultValue() + "L);");
        }

        if (it.example() != null) {
            lines.add(varName + ".example(" + it.example() + "L);");
        }

        var format = it.format();
        if (format != null && format.forType() == SchemaFormat.For.INTEGER) {
            lines.add(varName + ".format(SchemaFormat." + format + ");");
        }


        if (it.multipleOf() != null) {
            lines.add(varName + ".multipleOf(" + it.multipleOf() + "L);");
        }
        if (it.maximum() != null) {
            lines.add(varName + ".maximum(" + it.maximum() + "L);");
        }
        if (it.minimum() != null) {
            lines.add(varName + ".minimum(" + it.minimum() + "L);");
        }
        if (it.exclusiveMaximum() != null) {
            lines.add(varName + ".exclusiveMaximum(" + it.exclusiveMaximum() + "L);");
        }
        if (it.exclusiveMinimum() != null) {
            lines.add(varName + ".exclusiveMinimum(" + it.exclusiveMinimum() + "L);");
        }
    }
}
